package com.xyzboom.compiler.util

import com.xyzboom.compiler.compiler.grammar.ASTNode
import com.xyzboom.compiler.fa.DFA
import com.xyzboom.compiler.fa.NFA
import com.xyzboom.compiler.util.gui.ImageWindow
import guru.nidi.graphviz.attribute.*
import guru.nidi.graphviz.engine.Format
import guru.nidi.graphviz.graph
import guru.nidi.graphviz.model.MutableGraph
import guru.nidi.graphviz.toGraphviz
import java.awt.Dimension
import java.awt.EventQueue
import java.util.*
import kotlin.system.exitProcess

fun <T : Any, K : Any> NFA<T, K>.toGraphViz() =
    graph(directed = true) {
        for (node in allNodes) {
            if (node == nodeOut) {
                node.value.toString()[Shape.DOUBLE_CIRCLE]
            } else {
                node.value.toString()[Shape.CIRCLE]
            }
        }
        for (node in allNodes) {
            for (nextNodeEntry in node.next) {
                val nextNodes = nextNodeEntry.value
                for (nextNode in nextNodes) {
                    (node.value.toString() - nextNode.value.toString())[Label.of(nextNodeEntry.key.toString())]
                }
            }
        }
    }

fun <T : Any, K : Any> DFA<T, K>.toGraphViz() =
    graph(directed = true) {
        for (node in allNodes) {
            if (endNodes.contains(node)) {
                node.value.toString()[Shape.DOUBLE_CIRCLE]
            } else {
                node.value.toString()[Shape.CIRCLE]
            }
        }
        for (node in allNodes) {
            for (nextNodeEntry in node.next) {
                val nextNode = nextNodeEntry.value
                (node.value.toString() - nextNode.value.toString())[Label.of(nextNodeEntry.key.toString())]
            }
        }
    }

fun ASTNode.toGraphViz() =
    graph(directed = true) {
        val visited = HashSet<ASTNode>()
        val deque = ArrayDeque<ASTNode>()
        deque.add(this@toGraphViz)
        var id = 0
        val nodeStrMap = HashMap<ASTNode, String>()
        while (deque.isNotEmpty()) {
            val now = deque.removeFirst()
            if (visited.contains(now)) {
                continue
            }
            val nowStr = nodeStrMap.getOrPut(now) { ("${now.token?.info ?: now.typeName}@${id++}") }
            if (now.token == null) {
                nowStr[Color.RED]
            }
            for (node in now.children.reversed()) {
                val nodeStr =
                    nodeStrMap.getOrPut(node) { ("${node.token?.info ?: node.typeName}@${id++}") }
                if (node.token == null) {
                    nodeStr[Color.RED]
                }
                nowStr.replace("%", "").replace("\\", "") - nodeStr.replace("%", "").replace("\\", "")
                deque.add(node)
            }
            //这段子图用于调整语法树中节点的显示次序
            graph(directed = true) {
                graph[Rank.dir(Rank.RankDir.LEFT_TO_RIGHT)]
                graph[Rank.inSubgraph(Rank.RankType.SAME)]
                for (index in (1 until now.children.size).reversed()) {
                    val node = now.children[index]
                    val nextSiblingNode = now.children[index - 1]
                    val nodeStr = nodeStrMap[node]!!
                    val nextSiblingNodeStr = nodeStrMap[nextSiblingNode]!!
                    (nextSiblingNodeStr - nodeStr)[Style.INVIS]
                }
            }

            visited.add(now)
        }
    }

fun MutableGraph.show() {
    System.setProperty("java.awt.headless", "false")
    var windowClosed = false
    EventQueue.invokeLater {
        val img = this.toGraphviz().render(Format.PNG).toImage()
        ImageWindow(img).apply {
            size = Dimension(600, 400)
            isVisible = true
            closeCallback = {
                windowClosed = true
                exitProcess(0)
            }
        }
    }
    while (true) {//
        if (windowClosed) break
        Thread.sleep(1)
    }
}